Skip to content

Method: verifyCallbackParameterCount(Class, Method)

1: /**
2: * Copyright (C) 2022 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.model.metamodel;
16:
17: import cz.cvut.kbss.jopa.exception.MetamodelInitializationException;
18: import cz.cvut.kbss.jopa.model.annotations.EntityListeners;
19: import cz.cvut.kbss.jopa.model.lifecycle.LifecycleEvent;
20: import cz.cvut.kbss.jopa.utils.ReflectionUtils;
21:
22: import java.lang.reflect.Method;
23: import java.lang.reflect.Modifier;
24:
25: class EntityLifecycleCallbackResolver {
26:
27: private final AbstractIdentifiableType<?> managedType;
28: private final EntityLifecycleListenerManager manager;
29:
30: /**
31: * Creates a lifecycle callback resolver for the specified entity type.
32: * @param et The type to process
33: */
34: EntityLifecycleCallbackResolver(AbstractIdentifiableType<?> et) {
35: this.managedType = et;
36: this.manager = new EntityLifecycleListenerManager();
37: }
38:
39: /**
40: * Builds an instance of {@link EntityLifecycleListenerManager} for the specified managed type.
41: * <p>
42: * The manager contains:
43: * <ul>
44: * <li>Lifecycle callbacks declared in the entity class,</li>
45: * <li>EntityListener instance declared on the entity class,</li>
46: * <li>Reference to parent {@link EntityLifecycleListenerManager} (if exists).</li>
47: * </ul>
48: *
49: * @return Lifecycle listener manager instance
50: */
51: EntityLifecycleListenerManager resolve() {
52: resolveLifecycleCallbacks();
53: resolveEntityListeners();
54: if (managedType.getSupertype() != null) {
55: manager.setParent(managedType.getSupertype().getLifecycleListenerManager());
56: }
57: return manager;
58: }
59:
60: private void resolveLifecycleCallbacks() {
61: final Class<?> cls = managedType.getJavaType();
62: for (Method m : cls.getDeclaredMethods()) {
63: for (LifecycleEvent hookType : LifecycleEvent.values()) {
64: if (m.getDeclaredAnnotation(hookType.getAnnotation()) != null) {
65: verifyCallbackNotAlreadyDefined(hookType);
66: verifyLifecycleCallbackSignature(m);
67: manager.addLifecycleCallback(hookType, m);
68: }
69: }
70: }
71: }
72:
73: private void verifyCallbackNotAlreadyDefined(LifecycleEvent hookType) {
74: if (manager.hasLifecycleCallback(hookType)) {
75: throw new MetamodelInitializationException("The type [" + managedType.getJavaType().getName() +
76: "] has multiple lifecycle callbacks for the lifecycle event [" + hookType + "].");
77: }
78: }
79:
80: private void verifyLifecycleCallbackSignature(Method callback) {
81: if (callback.getParameterCount() > 0) {
82: throw MetamodelInitializationException
83: .invalidArgumentsForLifecycleListener(managedType.getJavaType(), callback);
84: }
85: if (!callback.getReturnType().equals(Void.TYPE)) {
86: throw MetamodelInitializationException
87: .invalidReturnTypeForLifecycleListener(managedType.getJavaType(), callback);
88: }
89: if (Modifier.isFinal(callback.getModifiers()) || Modifier.isStatic(callback.getModifiers())) {
90: throw MetamodelInitializationException
91: .invalidLifecycleListenerModifier(managedType.getJavaType(), callback);
92: }
93: }
94:
95: private void resolveEntityListeners() {
96: final EntityListeners listenersAnn = managedType.getJavaType().getDeclaredAnnotation(EntityListeners.class);
97: if (listenersAnn == null) {
98: return;
99: }
100: for (Class<?> listenerType : listenersAnn.value()) {
101: try {
102: final Object listener = ReflectionUtils.instantiateUsingDefaultConstructor(listenerType);
103: manager.addEntityListener(listener);
104: resolveEntityListenerCallbacks(listener, listenerType);
105: } catch (cz.cvut.kbss.jopa.exception.InstantiationException e) {
106: throw new MetamodelInitializationException("Unable to instantiate entity listener of type "
107: + listenerType + ". The listener has to have a public no-arg constructor.");
108: }
109: }
110: }
111:
112: private void resolveEntityListenerCallbacks(Object listener, Class<?> listenerType) {
113: for (Method m : listenerType.getDeclaredMethods()) {
114: for (LifecycleEvent hookType : LifecycleEvent.values()) {
115: if (m.getDeclaredAnnotation(hookType.getAnnotation()) != null) {
116: verifyEntityListenerCallbackNotAlreadyDefined(listener, listenerType, hookType);
117: verifyEntityListenerCallbackSignature(listenerType, m);
118: manager.addEntityListenerCallback(listener, hookType, m);
119: }
120: }
121: }
122: }
123:
124: private void verifyEntityListenerCallbackNotAlreadyDefined(Object listener, Class<?> listenerType,
125: LifecycleEvent event) {
126: if (manager.hasEntityListenerCallback(listener, event)) {
127: throw new MetamodelInitializationException("The entity listener [" + listenerType.getName() +
128: "] has multiple callbacks for the lifecycle event [" + event + "].");
129: }
130: }
131:
132: private void verifyEntityListenerCallbackSignature(Class<?> listenerType, Method callback) {
133: verifyCallbackParameterCount(listenerType, callback);
134: verifyCallbackParameterTypes(listenerType, callback);
135: verifyCallbackReturnType(listenerType, callback);
136: verifyCallbackModifiers(listenerType, callback);
137: }
138:
139: private static void verifyCallbackModifiers(Class<?> listenerType, Method callback) {
140: if (Modifier.isFinal(callback.getModifiers()) || Modifier.isStatic(callback.getModifiers())) {
141: throw MetamodelInitializationException.invalidEntityListenerCallbackModifier(listenerType, callback);
142: }
143: }
144:
145: private static void verifyCallbackReturnType(Class<?> listenerType, Method callback) {
146: if (!callback.getReturnType().equals(Void.TYPE)) {
147: throw MetamodelInitializationException.invalidReturnTypeForEntityListenerCallback(listenerType, callback);
148: }
149: }
150:
151: private void verifyCallbackParameterTypes(Class<?> listenerType, Method callback) {
152: final Class<?> paramType = callback.getParameterTypes()[0];
153: if (!paramType.isAssignableFrom(Object.class) && !paramType.isAssignableFrom(managedType.getJavaType())) {
154: throw MetamodelInitializationException
155: .invalidEntityListenerCallbackParameterType(managedType.getJavaType(), listenerType, callback);
156: }
157: }
158:
159: private static void verifyCallbackParameterCount(Class<?> listenerType, Method callback) {
160:• if (callback.getParameterCount() != 1) {
161: throw MetamodelInitializationException
162: .invalidArgumentsForEntityListenerCallback(listenerType, callback);
163: }
164: }
165: }